import { BodyItemConstant } from '../constants/InputItemConstants';
import { CirculatoryDiseasesAdvicesConstants } from '../constants/CirculatoryDiseasesAdvicesConstants';
import CommonUtils from './CommonUtils';
import AdvicesLogic from './CirculatoryDiseasesAdvicesLogic';
import AdvicesUtils from './CirculatoryDiseasesAdvicesUtils';

const {
  GENDER,
  SMOKING_HABIT,
  DIABETES_CURE_FLG,
  BLOOD_SUGAR_LEVEL_FASTING,
  BLOOD_SUGAR_LEVEL_ANYTIME,
  ANTIHYPERTENSIVE_FLG,
} = BodyItemConstant;

const {CALCULATE_RISK_LOW_FLG} = CirculatoryDiseasesAdvicesConstants;

class CardiacInfarctionLogic {

  /**
   * リスク計算
   * @param {Object.<number>} inputData 入力値(性別、年齢、最高血圧、降圧剤、喫煙習慣、HDLコレステロール、LDLコレステロール、総コレステロール、中性脂肪、糖尿病の治療の有無、血糖値（空腹時）【mg/dl】、血糖値（随時）【mg/dl】)
   * @returns リスク計算結果
   */
  static calcRisk({gender, age, maxBloodPressure, antihypertensiveFlg, smokingHabit, hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride, diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime}) {
    // 69歳を超えた場合、69歳とみなす
    age = CommonUtils.convertAge(age);

    // 中間値生成(喫煙フラグ、non-HDL値、糖尿病フラグ)
    const { genderValue, smokingValue, nonHdlValue, diabetesMellitusFlg } = this._calcMiddleParameter(gender, smokingHabit, hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride, diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime);

    // min, max用 血圧、HDL、NonHDLの値
    const { minBP, minHdl, minNonHdl, maxBP, maxHdl, maxNonHdl } = this._calcMaxMinMiddleParameter(genderValue, age);

    // probability:あなたのリスク、最小、最大
    const results = {
      probability: {
        you: this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesMellitusFlg, hdlCholesterol),
        min: this._getProbability(genderValue, age, minBP, ANTIHYPERTENSIVE_FLG.NO.value, 0, minNonHdl, 0, minHdl),
        max: this._getProbability(genderValue, age, maxBP, ANTIHYPERTENSIVE_FLG.YES.value, SMOKING_HABIT.IS_SMOKING.value, maxNonHdl, 1, maxHdl),
      },
    };
    return results;
  }

  /**
   * リスク因子（喫煙、non-HDL値、糖尿病フラグ）を加えた心筋梗塞リスク算出
   * @param {number} gender              性別
   * @param {number} age                 年齢
   * @param {number} maxBloodPressure    最高血圧
   * @param {number} antihypertensiveFlg 降圧剤
   * @param {number} smokingValue        喫煙習慣
   * @param {number} nonHdlValue         non-HDL値
   * @param {number} diabetesCureFlg     糖尿病フラグ
   * @param {number} hdlCholesterol      HDLコレステロール【mg/dl】
   * @returns リスク計算結果
   */
  static _getProbability(gender, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesCureFlg, hdlCholesterol) {
    const s = this._predLin(gender, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesCureFlg, hdlCholesterol);

    return 100 * (1 - Math.pow(0.99248, Math.exp(s - 36.44219638)));
  }

  /**
   * リスク因子（喫煙、non-HDL値、糖尿病フラグ）を加えた式1
   * @param {number} gender              性別
   * @param {number} age                 年齢
   * @param {number} maxBloodPressure    最高血圧
   * @param {number} antihypertensiveFlg 降圧剤
   * @param {number} smokingValue        喫煙習慣
   * @param {number} nonHdlValue         non-HDL値
   * @param {number} diabetesCureFlg     糖尿病フラグ
   * @param {number} hdlCholesterol      HDLコレステロール【mg/dl】
   * @returns リスク式1計算結果
   */
  static _predLin(gender, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesCureFlg, hdlCholesterol) {
    return 5.59261 * Math.log(age) +
           12.52568 * gender +
           2.09871 * Math.log(maxBloodPressure) +
           10.28406 * antihypertensiveFlg +
           0.69074 * smokingValue +
           1.46938 * Math.log(nonHdlValue) +
           (-1.100) * Math.log(hdlCholesterol) +
           0.55877 * diabetesCureFlg +
           (-2.79701) * Math.log(age) * gender +
           (-1.95968) * Math.log(maxBloodPressure) * antihypertensiveFlg; 
  }

  /**
   * 計算用の中間値生成(喫煙フラグ、non-HDL値、糖尿病フラグ )
   * @param {number} smokingHabit            喫煙習慣
   * @param {number} hdlCholesterol          HDLコレステロール
   * @param {number} ldlCholesterol          LDLコレステロール
   * @param {number} totalCholesterol        総コレステロール
   * @param {number} triglyceride            中性脂肪
   * @param {number} diabetesCureFlg         糖尿病の治療の有無
   * @param {number} bloodSugarLevelFasting  血糖値（空腹時）【mg/dl】
   * @param {number} bloodSugarLevelAnytime  血糖値（随時）【mg/dl】
   * @returns 中間値(喫煙の有無、non-HDL値、糖尿病フラ)
   */
  static _calcMiddleParameter(gender, smokingHabit, hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride, diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime) {

    // 性別
    const genderValue = this._getGenderValue(gender);

    // 喫煙の有無
    const smokingValue = this._getSmokingValue(smokingHabit);

    // non-HDL値
    const nonHdlValue = this._getNonHdlValue(hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride);

    // 糖尿病フラグ
    const diabetesMellitusFlg = this._getDiabetesCureValue(diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime);

    return { genderValue, smokingValue, nonHdlValue, diabetesMellitusFlg };
  }

  /**
   * 性別および年代別の min, max用 血圧、HDL、NonHDLの値を取得
   * @param {number} genderValue 性別
   * @param {number} age         年齢
   * @returns min, max用中間値(血圧、HDLコレステロール、non-HDL値)
   */
  static _calcMaxMinMiddleParameter(genderValue, age) {
    // 1次元  0:女性、1:男性
    // 2次元  0:50才未満、1:50才以上,60才未満、2:60才以上
    const DEF_MIN_BP = [[95, 100, 95], [100, 100, 100]]; // 血圧
    const DEF_MIN_HDL = [[95, 90, 90], [90, 90, 90]];    // HDL
    const DEF_MIN_NONHDL = [[80, 90, 90], [75, 80, 75]]; // NonHdl

    const DEF_MAX_BP = [[160, 170, 180], [170, 175, 180]];
    const DEF_MAX_HDL = [[35, 35, 35], [30, 30, 30]];
    const DEF_MAX_NONHDL = [[210, 230, 230], [240, 220, 220]];

    const ageIndex = this._getAgeIndex(age);

    return {
      minBP: DEF_MIN_BP[genderValue][ageIndex],
      minHdl: DEF_MIN_HDL[genderValue][ageIndex],
      minNonHdl: DEF_MIN_NONHDL[genderValue][ageIndex],
      maxBP: DEF_MAX_BP[genderValue][ageIndex],
      maxHdl: DEF_MAX_HDL[genderValue][ageIndex],
      maxNonHdl: DEF_MAX_NONHDL[genderValue][ageIndex],
    };
  }

  /**
   * 属する世代のコード値を取得
   * @param {number} age 年齢
   * @returns 0:50才未満、1:50才以上,60才未満、2:60才以上
   */
  static _getAgeIndex(age) {
    let ageIndex;
    if (age < 50) {
      ageIndex = 0;
    } else if (age < 60) {
      ageIndex = 1;
    } else {
      ageIndex = 2;
    }
    return ageIndex;
  }

  /**
   * 画面入力値 男性：１、女性：２
   * @param {number} gender 
   * @returns １：男性、０：女性
   */
  static _getGenderValue(gender) {
    return gender === GENDER.MALE.value ? 1 : 0;
  }

  /**
   * 喫煙の有無
   * @param {number} smokingHabit
   * @returns ０:ない 1:ある
   */
  static _getSmokingValue(smokingHabit) {
    return smokingHabit > SMOKING_HABIT.IS_SMOKING.value ? 0 : 1;
  }

  /**
   * HDLコレステロールにより、non-HDL値を生成する
   * @param {number} hdlCholesterol   HDLコレステロール
   * @param {number} ldlCholesterol   LDLコレステロール
   * @param {number} totalCholesterol 総コレステロール
   * @param {number} triglyceride     中性脂肪
   * @returns non-HDL値
   */
  static _getNonHdlValue(hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride) {
    // LDLコレステロール値 > 0 且つ 中性脂肪 > 0 の場合
    if (ldlCholesterol > 0 && triglyceride > 0) {
      // LDLコレステロール + 中性脂肪 / 5
      return ldlCholesterol + triglyceride / 5;
    } else {
      // 総コレステロール  -  HDLコレステロール
      return totalCholesterol - hdlCholesterol;
    }
  }

  /**
   * 糖尿病フラグを取得する
   * @param {number} diabetesCureFlg         糖尿病の治療の有無
   * @param {number} bloodSugarLevelFasting  血糖値（空腹時）【mg/dl】
   * @param {number} bloodSugarLevelAnytime  血糖値（随時）【mg/dl】
   * @returns diabetesCureFlg 糖尿病フラグ
   */
  static _getDiabetesCureValue(diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime) {
    // 糖尿病フラグ初期値は0
    let diabetesMellitusFlg = 0;

    // 糖尿病の治療あり
    if (diabetesCureFlg === DIABETES_CURE_FLG.YES.value) {
      diabetesMellitusFlg = 1;
    } else {
      // 空腹時
      if (bloodSugarLevelFasting !== undefined) {
        // 血糖値が126mg/dl以上の場合
        if (bloodSugarLevelFasting >= BLOOD_SUGAR_LEVEL_FASTING.HIGH.value) {
          diabetesMellitusFlg = 1;
        }
      // 随時
      } else if (bloodSugarLevelAnytime !== undefined) {
        // 血糖値が200mg/dl以上の場合
        if (bloodSugarLevelAnytime >= BLOOD_SUGAR_LEVEL_ANYTIME.HIGH.value) {
          diabetesMellitusFlg = 1;
        }
      }
    }

    return diabetesMellitusFlg;
  }

  /**
   * アドバイスを生成する
   * @param {Object.<number>} inputData 入力値(性別
   *                                          年齢
   *                                          身長（cm）
   *                                          体重（kg）
   *                                          喫煙習慣
   *                                          糖尿病の治療の有無
   *                                          血糖値（空腹時）【mg/dl】
   *                                          血糖値（随時）【mg/dl】
   *                                          降圧剤
   *                                          最高血圧(収縮期)
   *                                          最低血圧(拡張期)
   *                                          HDLコレステロール
   *                                          LDLコレステロール
   *                                          総コレステロール
   *                                          中性脂肪)
   * @returns アドバイス
   * 
   */
  static takeAdvices({
                      gender,
                      age,
                      bodyHeight,
                      bodyWeight,
                      smokingHabit,
                      diabetesCureFlg,
                      bloodSugarLevelFasting,
                      bloodSugarLevelAnytime,
                      antihypertensiveFlg,
                      maxBloodPressure,
                      minBloodPressure,
                      hdlCholesterol,
                      ldlCholesterol,
                      totalCholesterol,
                      triglyceride}) {

    // 69歳を超えた場合、69歳とみなす
    age = CommonUtils.convertAge(age); 
  
    // アドバイス中間パラメタを取得する
    const { 
      genderValue, 
      smokingValue, 
      bmi,
      isHypertension,
      bloodSugarGrades,
      SugartoleranceGrades,
      maxBloodPressureGrades,
      minBloodPressureGrades,
      nonHdlValue,
      isHypoglycemia,
      isGoodHDL,
      isGoodTG,
      isGoodNonHdl,
      isCalculateRiskByBloodSugar,
      isCalculateRiskByBloodPressure,
      isCalculateRiskByLipid,
      riskBaseParameter,
    } = AdvicesUtils._calcAdvicesMiddleParameter(bodyHeight,
                                                 bodyWeight,
                                                 gender,
                                                 age,
                                                 smokingHabit,
                                                 antihypertensiveFlg,
                                                 maxBloodPressure,
                                                 minBloodPressure,
                                                 bloodSugarLevelFasting,
                                                 bloodSugarLevelAnytime,
                                                 hdlCholesterol,
                                                 ldlCholesterol,
                                                 totalCholesterol,
                                                 triglyceride,
                                                 diabetesCureFlg);

    // BMIアドバイス
    const {bmiId, bmiAdvices} = AdvicesLogic._getBMIAdvices(bmi);
    // 血糖値アドバイス
    const {bloodSugarId, bloodSugarAdvices} = AdvicesLogic._getBloodSugarAdvices(diabetesCureFlg, bloodSugarGrades);
    // 血圧アドバイス
    const {bloodPressureId, bloodPressureAdvices} = AdvicesLogic._getBloodPressureAdvices(antihypertensiveFlg, maxBloodPressureGrades, minBloodPressureGrades);
    // 血中脂質アドバイス
    const {bloodLipidsId, bloodLipidsAdvices} = AdvicesLogic._getBloodLipidsAdvices(genderValue, smokingValue, isHypertension, isHypoglycemia, isGoodHDL, isGoodTG, isGoodNonHdl);
    // タバコアドバイス
    const {smokingId, smokingAdvices} = AdvicesLogic._getSmokingAdvices(smokingValue);

    let isAllGood = 0;

    // 血糖、血圧、たばこ、脂質、BMIのすべてが問題なしの場合
    if (bmiId === 'BMI_LESS_THAN_25' &&
        bloodSugarId === 'ND' &&
        bloodPressureId === 'NH' &&
        bloodLipidsId === 'GOOD' &&
        smokingId === 'NS') {

      isAllGood = 1;
    }

    // すすめの生活習慣のID
    const key = AdvicesLogic._getRecommendationId(isGoodHDL, isGoodTG, isGoodNonHdl, triglyceride, bmi, smokingValue, isHypertension, diabetesCureFlg, SugartoleranceGrades);
    // すすめの生活習慣の内容を取得する
    const recommendation = AdvicesLogic._getRecommendationContent(key);

    // 元糖尿病フラグ
    const diabetesMellitusFlg = this._getDiabetesCureValue(diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime);
    // あなたのリスク
    const you = this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesMellitusFlg, hdlCholesterol);
    // 十分に低いリスクの基準リスクを計算する
    const riskBase = this.calcBaseRisk(riskBaseParameter);
    
    if (you <= riskBase) {
      return {
        // すべてが問題なし
        allGood : {
          isAllGood : isAllGood,
        },

        // アドバイス
        Advices : {
          // BMIアドバイス
          bmiAdvices : bmiAdvices,
          // 血糖値アドバイス
          bloodSugarAdvices : bloodSugarAdvices,
          // 血圧アドバイス
          bloodPressureAdvices : bloodPressureAdvices,
          // 血中脂質アドバイス
          bloodLipidsAdvices : bloodLipidsAdvices,
          // タバコアドバイス
          smokingAdvices : smokingAdvices,
        },

        // すすめの生活習慣の内容
        habit : {
          recommendation : recommendation,
        },

        // リスク改善
        improveRisk : {
          // 現在のリスク
          you,
          // 改善血糖値
          improveBloodSugarRisk : undefined,
          // 改善血圧のコントロール
          improveBloodPressureRisk : undefined,
          // 禁煙する
          improveSmokingRisk : undefined,
          // 改善脂質
          improveLipidRisk : undefined,
          // すべて改善
          improveAll  : undefined,
          // 改善必要ではない
          improveCount : 0,
        },
      };
    }

    // 改善項目数
    let improveCount = 0;

    // 血糖値良好リスク
    let goodBloodSugarRisk;
    // 糖尿病フラグ
    let goodBloodSugarFlg;

    // 血糖値が良好になると、あなたのリスクが最大低下
    if (isCalculateRiskByBloodSugar === CALCULATE_RISK_LOW_FLG.YES) {
      
      // 空腹時
      goodBloodSugarFlg = this._getDiabetesCureValue(diabetesCureFlg, BLOOD_SUGAR_LEVEL_FASTING.LOW.value, undefined);

      goodBloodSugarRisk = this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, goodBloodSugarFlg, hdlCholesterol);

      // 改善前と改善後一致ではない場合
      if (you !== goodBloodSugarRisk) {
        improveCount++;
      }
    }


    // 血圧値良好
    let goodBloodPressure;
    // 血圧値良好リスク
    let goodBloodPressureRisk;
    // 血圧のコントロールが良好になると、あなたのリスクが最大低下
    if (isCalculateRiskByBloodPressure === CALCULATE_RISK_LOW_FLG.YES) {

      goodBloodPressure = maxBloodPressure >= 140 ? 100 : maxBloodPressure;

      goodBloodPressureRisk = this._getProbability(genderValue, age, goodBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesMellitusFlg, hdlCholesterol);

      // 改善前と改善後一致ではない場合
      if (you !== goodBloodPressureRisk) {
        improveCount++;
      }
    }

    // 禁煙
    let noSmoking;
    // 禁煙リスク
    let goodSmokingRisk;
    // 禁煙すると、あなたのリスクが最大低下
    if (smokingValue === SMOKING_HABIT.IS_SMOKING.value) {

      noSmoking = this._getSmokingValue(SMOKING_HABIT.NO_SMOKING.value);

      goodSmokingRisk = this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, noSmoking, nonHdlValue, diabetesMellitusFlg, hdlCholesterol);

      // 改善前と改善後一致ではない場合
      if (you !== goodSmokingRisk) {
        improveCount++;
      }
    }


    // HDLコレステロール値良好
    let goodHdl;
    // non-HDL良好
    let goodNonHdlValue;
    // 脂質良好リスク
    let goodLipidRisk;
    // 脂質のコントロールが良好になると、あなたのリスクが最大低下
    if (isCalculateRiskByLipid === CALCULATE_RISK_LOW_FLG.YES) {

      // HDLとNolHdlを取得する
      const { minHdl, minNonHdl } = this._calcMaxMinMiddleParameter(genderValue, age);
      goodHdl = minHdl;

      goodNonHdlValue = minNonHdl;

      goodLipidRisk = this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, smokingValue, goodNonHdlValue, diabetesMellitusFlg, goodHdl);

      // 改善前と改善後一致ではない場合
      if (you !== goodLipidRisk) {
        improveCount++;
      }
    }


    // すべて良好
    let goodRisk;
    // 改善項目数が一つ以上の場合
    if (improveCount > 1) {
      goodRisk = this._getProbability(genderValue,
                                      age,
                                      isCalculateRiskByBloodPressure === CALCULATE_RISK_LOW_FLG.YES ? goodBloodPressure : maxBloodPressure,
                                      antihypertensiveFlg,
                                      smokingValue === SMOKING_HABIT.IS_SMOKING.value ? noSmoking : smokingValue,
                                      isCalculateRiskByLipid === CALCULATE_RISK_LOW_FLG.YES ? goodNonHdlValue : nonHdlValue,
                                      isCalculateRiskByBloodSugar === CALCULATE_RISK_LOW_FLG.YES ? goodBloodSugarFlg : diabetesMellitusFlg, 
                                      isCalculateRiskByLipid === CALCULATE_RISK_LOW_FLG.YES ? goodHdl : hdlCholesterol);
    }

    const results = {
      // すべてが問題なし
      allGood : {
        isAllGood : isAllGood,
      },

      // アドバイス
      Advices : {
        // BMIアドバイス
        bmiAdvices : bmiAdvices,
        // 血糖値アドバイス
        bloodSugarAdvices : bloodSugarAdvices,
        // 血圧アドバイス
        bloodPressureAdvices : bloodPressureAdvices,
        // 血中脂質アドバイス
        bloodLipidsAdvices : bloodLipidsAdvices,
        // タバコアドバイス
        smokingAdvices : smokingAdvices,
      },

      // すすめの生活習慣の内容
      habit : {
        recommendation : recommendation,
      },

      // リスク改善
      improveRisk : {
        // 現在のリスク
        you,
        // 改善血糖値
        improveBloodSugarRisk : (isCalculateRiskByBloodSugar === CALCULATE_RISK_LOW_FLG.YES && goodBloodSugarRisk !== you) ? goodBloodSugarRisk : undefined,
        // 改善血圧のコントロール
        improveBloodPressureRisk : (isCalculateRiskByBloodPressure === CALCULATE_RISK_LOW_FLG.YES && goodBloodPressureRisk !== you) ? goodBloodPressureRisk : undefined,
        // 禁煙する
        improveSmokingRisk : (smokingValue === SMOKING_HABIT.IS_SMOKING.value && goodSmokingRisk !== you) ? goodSmokingRisk : undefined,
        // 改善脂質
        improveLipidRisk : (isCalculateRiskByLipid === CALCULATE_RISK_LOW_FLG.YES && goodLipidRisk) ? goodLipidRisk : undefined,
        // すべて改善
        improveAll : (improveCount > 1 && goodRisk !== you) ? goodRisk : undefined,
        // 改善必要ではない
        improveCount : improveCount,
      },
    }

    return results;
  }

 /**
   * リスク計算(十分に低い)
   * @param {Object.<number>} 十分に低いリスクの基準データ(性別、年齢、最高血圧、降圧剤、喫煙習慣、HDLコレステロール、LDLコレステロール、総コレステロール、中性脂肪、糖尿病の治療の有無、血糖値（空腹時）【mg/dl】、血糖値（随時）【mg/dl】)
   * @returns リスク計算結果
   */
  static calcBaseRisk({gender, age, maxBloodPressure, antihypertensiveFlg, smokingHabit, hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride, diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime, nonHdlValue}) {
    // 69歳を超えた場合、69歳とみなす
    age = CommonUtils.convertAge(age);

    // 中間値生成(喫煙フラグ、non-HDL値、糖尿病フラグ)
    const { genderValue, smokingValue, diabetesMellitusFlg } = this._calcMiddleParameter(gender, smokingHabit, hdlCholesterol, ldlCholesterol, totalCholesterol, triglyceride, diabetesCureFlg, bloodSugarLevelFasting, bloodSugarLevelAnytime);


    return this._getProbability(genderValue, age, maxBloodPressure, antihypertensiveFlg, smokingValue, nonHdlValue, diabetesMellitusFlg, hdlCholesterol);
  }
}

export default CardiacInfarctionLogic
